package com.bookstore.service.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bookstore.common.constant.UserConstants;
import com.bookstore.common.exception.BusinessException;
import com.bookstore.common.util.IpUtil;
import com.bookstore.common.util.PasswordUtil;
import com.bookstore.framework.security.JwtTokenUtil;
import com.bookstore.framework.security.TokenService;
import com.bookstore.model.dto.LoginDTO;
import com.bookstore.model.dto.RegisterDTO;
import com.bookstore.model.entity.AuthUser;
import com.bookstore.model.entity.UserLoginLog;
import com.bookstore.model.entity.UserProfile;
import com.bookstore.model.vo.LoginVO;
import com.bookstore.model.vo.RegisterVO;
import com.bookstore.service.mapper.AuthUserMapper;
import com.bookstore.service.mapper.UserLoginLogMapper;
import com.bookstore.service.mapper.UserProfileMapper;
import com.bookstore.service.service.AuthService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

/**
 * 认证服务实现类
 */
@Service
public class AuthServiceImpl implements AuthService {

    private static final Logger logger = LoggerFactory.getLogger(AuthServiceImpl.class);

    @Autowired
    private AuthUserMapper authUserMapper;

    @Autowired
    private UserProfileMapper userProfileMapper;

    @Autowired
    private UserLoginLogMapper userLoginLogMapper;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private TokenService tokenService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoginVO login(LoginDTO loginDTO, HttpServletRequest request) {
        // 1. 根据用户名/手机号/邮箱查询用户
        AuthUser authUser = getAuthUser(loginDTO.getUsername());
        if (authUser == null) {
            // 记录登录失败日志
            recordLoginLog(null, loginDTO.getUsername(), UserConstants.LOGIN_TYPE_PASSWORD,
                    UserConstants.LOGIN_RESULT_FAILED, "用户不存在", request);
            throw new BusinessException("用户名或密码错误");
        }

        // 2. 校验用户类型
        if (loginDTO.getUserType() != null && !loginDTO.getUserType().equals(authUser.getUserType())) {
            // 记录登录失败日志
            recordLoginLog(authUser.getId(), loginDTO.getUsername(), UserConstants.LOGIN_TYPE_PASSWORD,
                    UserConstants.LOGIN_RESULT_FAILED, "用户类型不匹配", request);
            throw new BusinessException("用户类型不匹配");
        }

        // 3. 校验用户状态
        if (UserConstants.USER_STATUS_NORMAL != authUser.getStatus()) {
            String reason = UserConstants.USER_STATUS_DISABLED == authUser.getStatus() ? "账号已禁用" : "账号已锁定";
            // 记录登录失败日志
            recordLoginLog(authUser.getId(), loginDTO.getUsername(), UserConstants.LOGIN_TYPE_PASSWORD,
                    UserConstants.LOGIN_RESULT_FAILED, reason, request);
            throw new BusinessException(reason);
        }

        // 4. 校验密码
        if (!PasswordUtil.verifyPassword(loginDTO.getPassword(), authUser.getSalt(), authUser.getPassword())) {
            // 记录登录失败日志
            recordLoginLog(authUser.getId(), loginDTO.getUsername(), UserConstants.LOGIN_TYPE_PASSWORD,
                    UserConstants.LOGIN_RESULT_FAILED, "密码错误", request);
            throw new BusinessException("用户名或密码错误");
        }

        // 5. 更新登录信息
        String loginIp = IpUtil.getIpAddr(request);
        authUserMapper.updateLoginInfo(authUser.getId(), loginIp);

        // 6. 记录登录成功日志
        recordLoginLog(authUser.getId(), loginDTO.getUsername(), UserConstants.LOGIN_TYPE_PASSWORD,
                UserConstants.LOGIN_RESULT_SUCCESS, null, request);

        // 7. 获取用户详细信息
        UserProfile userProfile = userProfileMapper.selectByAuthId(authUser.getId());

        // 8. 生成token并存储到Redis
        String token = tokenService.createToken(authUser.getId(), authUser.getUsername(), authUser.getUserType());

        // 9. 构建返回结果
        LoginVO loginVO = new LoginVO();
        loginVO.setUserId(authUser.getId());
        loginVO.setUsername(authUser.getUsername());
        loginVO.setUserType(authUser.getUserType());
        loginVO.setToken(token);

        if (userProfile != null) {
            loginVO.setNickname(userProfile.getNickname());
            loginVO.setAvatar(userProfile.getAvatar());
            loginVO.setLevel(userProfile.getLevel());
        }

        return loginVO;
    }

    @Override
    public void logout(Long userId) {
        // 从Redis中删除token
        tokenService.removeToken(userId);
        logger.info("用户退出登录: {}", userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RegisterVO register(RegisterDTO registerDTO, HttpServletRequest request) {
        // 1. 校验两次密码是否一致
        if (!registerDTO.getPassword().equals(registerDTO.getConfirmPassword())) {
            throw new BusinessException("两次输入的密码不一致");
        }

        // 2. 校验用户名是否已存在
        if (checkUsernameExists(registerDTO.getUsername())) {
            throw new BusinessException("用户名已存在");
        }

        // 3. 校验手机号是否已存在
        if (StrUtil.isNotBlank(registerDTO.getPhone()) && checkPhoneExists(registerDTO.getPhone())) {
            throw new BusinessException("手机号已被注册");
        }

        // 4. 校验邮箱是否已存在
        if (StrUtil.isNotBlank(registerDTO.getEmail()) && checkEmailExists(registerDTO.getEmail())) {
            throw new BusinessException("邮箱已被注册");
        }

        // 5. 校验用户类型
        if (registerDTO.getUserType() == null ||
            (registerDTO.getUserType() != UserConstants.USER_TYPE_NORMAL &&
             registerDTO.getUserType() != UserConstants.USER_TYPE_MERCHANT)) {
            throw new BusinessException("用户类型不正确");
        }

        // 6. 创建用户(手机号和邮箱可以为空)
        AuthUser authUser = new AuthUser();
        BeanUtils.copyProperties(registerDTO, authUser);
        authUser.setUsername(registerDTO.getUsername());
//        authUser.setPhone(registerDTO.getPhone());
//        authUser.setEmail(registerDTO.getEmail());
//        authUser.setUserType(registerDTO.getUserType());
        authUser.setStatus(UserConstants.USER_STATUS_NORMAL);
        authUser.setLoginCount(0);

        // 生成盐值和加密密码
        String salt = PasswordUtil.generateSalt();
        String encryptedPassword = PasswordUtil.encryptPassword(registerDTO.getPassword(), salt);
        authUser.setSalt(salt);
        authUser.setPassword(encryptedPassword);

        // 设置创建时间
        authUser.setCreatedTime(new Date());
        authUser.setUpdatedTime(new Date());

        // 保存用户
        authUserMapper.insert(authUser);

        // 7. 创建用户详情
        UserProfile userProfile = new UserProfile();
        userProfile.setAuthId(authUser.getId());
        userProfile.setNickname(registerDTO.getUsername()); // 默认使用用户名作为昵称
        userProfile.setLevel(1); // 默认等级为1
        userProfile.setPoints(0); // 默认积分为0
        userProfile.setTotalPoints(0); // 默认累计积分为0
        userProfile.setReadingTime(0); // 默认阅读时长为0
        userProfile.setCreatedTime(new Date());
        userProfile.setUpdatedTime(new Date());

        // 保存用户详情
        userProfileMapper.insert(userProfile);

        // 8. 记录登录日志
        String loginIp = IpUtil.getIpAddr(request);
        recordLoginLog(authUser.getId(), authUser.getUsername(), UserConstants.LOGIN_TYPE_PASSWORD,
                UserConstants.LOGIN_RESULT_SUCCESS, null, request);

        // 9. 生成token并存储到Redis
        String token = tokenService.createToken(authUser.getId(), authUser.getUsername(), authUser.getUserType());

        // 10. 构建返回结果
        RegisterVO registerVO = new RegisterVO();
        registerVO.setUserId(authUser.getId());
        registerVO.setUsername(authUser.getUsername());
        registerVO.setUserType(authUser.getUserType());
        registerVO.setToken(token);

        return registerVO;
    }

    @Override
    public boolean checkUsernameExists(String username) {
        if (StrUtil.isBlank(username)) {
            return false;
        }
        LambdaQueryWrapper<AuthUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AuthUser::getUsername, username)
                   .eq(AuthUser::getDeleted, 0);
        return authUserMapper.selectCount(queryWrapper) > 0;
    }

    @Override
    public boolean checkPhoneExists(String phone) {
        if (StrUtil.isBlank(phone)) {
            return false;
        }
        LambdaQueryWrapper<AuthUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AuthUser::getPhone, phone)
                   .eq(AuthUser::getDeleted, 0);
        return authUserMapper.selectCount(queryWrapper) > 0;
    }

    @Override
    public boolean checkEmailExists(String email) {
        if (StrUtil.isBlank(email)) {
            return false;
        }
        LambdaQueryWrapper<AuthUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AuthUser::getEmail, email)
                   .eq(AuthUser::getDeleted, 0);
        return authUserMapper.selectCount(queryWrapper) > 0;
    }

    /**
     * 根据用户名/手机号/邮箱查询用户
     * @param username 用户名/手机号/邮箱
     * @return 用户信息
     */
    private AuthUser getAuthUser(String username) {
        AuthUser authUser = null;

        // 1. 尝试按用户名查询
        authUser = authUserMapper.selectByUsername(username);

        // 2. 尝试按手机号查询
        if (authUser == null && StrUtil.isNotEmpty(username)) {
            authUser = authUserMapper.selectByPhone(username);
        }

        // 3. 尝试按邮箱查询
        if (authUser == null && StrUtil.isNotEmpty(username)) {
            authUser = authUserMapper.selectByEmail(username);
        }

        return authUser;
    }

    /**
     * 记录登录日志
     * @param userId 用户ID
     * @param username 用户名
     * @param loginType 登录类型
     * @param loginResult 登录结果
     * @param failureReason 失败原因
     * @param request HTTP请求
     */
    private void recordLoginLog(Long userId, String username, Integer loginType, Integer loginResult, String failureReason, HttpServletRequest request) {
        try {
            String userAgent = request.getHeader("User-Agent");
            String loginIp = IpUtil.getIpAddr(request);

            UserLoginLog loginLog = new UserLoginLog();
            loginLog.setUserId(userId);
            loginLog.setLoginType(loginType);
            loginLog.setLoginMethod("password");
            loginLog.setLoginIp(loginIp);
            // 这里可以根据IP获取地理位置，需要集成第三方服务
            // loginLog.setLoginLocation(getLocationByIp(loginIp));
            loginLog.setUserAgent(userAgent);
            loginLog.setDeviceType(IpUtil.getDeviceType(userAgent));
            loginLog.setBrowserType(IpUtil.getBrowserType(userAgent));
            loginLog.setOsType(IpUtil.getOsType(userAgent));
            loginLog.setLoginResult(loginResult);
            loginLog.setFailureReason(failureReason);
            loginLog.setSessionId(request.getSession().getId());
            loginLog.setCreatedTime(new Date());

            userLoginLogMapper.insert(loginLog);
        } catch (Exception e) {
            // 记录日志失败不影响主流程
            logger.error("记录登录日志失败: {}", e.getMessage(), e);
        }
    }
}